home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / tabContext.js < prev    next >
Text File  |  2010-01-15  |  13KB  |  456 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. const throttleTimeWindow = 200;
  9. const throttleMessageLimit = 30;
  10. const throttleInterval = 30;
  11. const throttleFlushCount = 20;
  12.  
  13. const refreshDelay = 300;
  14.  
  15. // ************************************************************************************************
  16.  
  17. Firebug.TabContext = function(win, browser, chrome, persistedState)
  18. {
  19.     this.window = win;
  20.     this.browser = browser;
  21.     this.persistedState = persistedState;
  22.  
  23.     browser.__defineGetter__("chrome", function() { return Firebug.chrome; }); // backward compat
  24.  
  25.     this.name = normalizeURL(this.getWindowLocation().toString());
  26.  
  27.     this.windows = [];
  28.     this.panelMap = {};
  29.     this.sidePanelNames = {};
  30.     this.sourceFileMap = {};
  31.  
  32.     // New nsITraceableChannel interface (introduced in FF3.0.4) makes possible
  33.     // to re-implement source-cache so, it solves the double-load problem.
  34.     // Anyway, keep the previous cache implementation for backward compatibility
  35.     // (with Firefox 3.0.3 and lower)
  36.     if (Components.interfaces.nsITraceableChannel)
  37.         this.sourceCache = new Firebug.TabCache(this);
  38.     else
  39.         this.sourceCache = new Firebug.SourceCache(this);
  40.  
  41.     this.global = win;  // used by chromebug
  42. };
  43.  
  44. Firebug.TabContext.prototype =
  45. {
  46.     getWindowLocation: function()
  47.     {
  48.         return safeGetWindowLocation(this.window);
  49.     },
  50.  
  51.     getTitle: function()
  52.     {
  53.         if (this.window && this.window.document)
  54.             return this.window.document.title;
  55.         else
  56.             return "";
  57.     },
  58.  
  59.     getName: function()
  60.     {
  61.         if (!this.name || this.name === "about:blank")
  62.         {
  63.             var url = this.getWindowLocation().toString();
  64.             if (isDataURL(url))
  65.             {
  66.                 var props = splitDataURL(url);
  67.                 if (props.fileName)
  68.                      this.name = "data url from "+props.fileName;
  69.             }
  70.             else
  71.             {
  72.                 this.name = normalizeURL(url);
  73.             }
  74.         }
  75.         return this.name;
  76.     },
  77.  
  78.     getGlobalScope: function()
  79.     {
  80.         return this.window;
  81.     },
  82.  
  83.     addSourceFile: function(sourceFile)
  84.     {
  85.         this.sourceFileMap[sourceFile.href] = sourceFile;
  86.         sourceFile.context = this;
  87.  
  88.         Firebug.onSourceFileCreated(this, sourceFile);
  89.     },
  90.  
  91.     removeSourceFile: function(sourceFile)
  92.     {
  93.         delete this.sourceFileMap[sourceFile.href];
  94.         delete sourceFile.context;
  95.  
  96.         // ?? Firebug.onSourceFileDestroyed(this, sourceFile);
  97.     },
  98.     // ***************************************************************************
  99.     get chrome()  // backward compat
  100.     {
  101.         return Firebug.chrome;
  102.     },
  103.  
  104.     reattach: function(oldChrome, newChrome)
  105.     {
  106.         for (var panelName in this.panelMap)
  107.         {
  108.             var panel = this.panelMap[panelName];
  109.             panel.detach(oldChrome, newChrome);
  110.             panel.invalid = true;// this will cause reattach on next use
  111.  
  112.             var panelNode = panel.panelNode;  // delete panel content
  113.             if (panelNode && panelNode.parentNode)
  114.                 panelNode.parentNode.removeChild(panelNode);
  115.         }
  116.     },
  117.  
  118.     destroy: function(state)
  119.     {
  120.         if (this.timeouts)
  121.         {
  122.             for (var timeout in this.timeouts)
  123.                 clearTimeout(timeout);
  124.         }
  125.  
  126.         if (this.intervals)
  127.         {
  128.             for (var timeout in this.intervals)
  129.                 clearInterval(timeout);
  130.         }
  131.  
  132.         if (this.throttleTimeout)
  133.             clearTimeout(this.throttleTimeout);
  134.  
  135.         state.panelState = {};
  136.  
  137.         // Inherit panelStates that have not been restored yet
  138.         if (this.persistedState)
  139.         {
  140.             for (var panelName in this.persistedState.panelState)
  141.                 state.panelState[panelName] = this.persistedState.panelState[panelName];
  142.         }
  143.  
  144.         for (var panelName in this.panelMap)
  145.         {
  146.             var panel = this.panelMap[panelName];
  147.  
  148.             // Create an object to persist state, re-using old one if it was never restored
  149.             var panelState = panelName in state.panelState ? state.panelState[panelName] : {};
  150.             state.panelState[panelName] = panelState;
  151.  
  152.             try
  153.             {
  154.                 // Destroy the panel and allow it to persist extra info to the state object
  155.                 panel.destroy(panelState);
  156.             }
  157.             catch(exc)
  158.             {
  159.                 delete state.panelState[panelName];
  160.             }
  161.  
  162.             // Remove the panel node from the DOM
  163.             var panelNode = panel.panelNode;  // delete panel content
  164.             if (panelNode && panelNode.parentNode)
  165.                 panelNode.parentNode.removeChild(panelNode);
  166.         }
  167.  
  168.         for (var name in this)
  169.             delete this[name];
  170.     },
  171.  
  172.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  173.  
  174.     addPanelType: function(url, title, parentPanel)
  175.     {
  176.         url = absoluteURL(url, this.window.location.href);
  177.         if (!url)
  178.         {
  179.             // XXXjoe Need some kind of notification to console that URL is invalid
  180.             throw("addPanelType: url is invalid!");
  181.             return;
  182.         }
  183.  
  184.         if (!this.panelTypes)
  185.         {
  186.             this.panelTypes = [];
  187.             this.panelTypeMap = {};
  188.         }
  189.  
  190.         var name = createPanelName(url);
  191.         while (name in this.panelTypeMap)
  192.             name += "_";
  193.  
  194.         var panelType = createPanelType(name, url, title, parentPanel);
  195.  
  196.         this.panelTypes.push(panelType);
  197.         this.panelTypeMap[name] = panelType;
  198.  
  199.         return panelType;
  200.     },
  201.  
  202.     removePanelType: function(url)
  203.     {
  204.         // NYI
  205.     },
  206.  
  207.     getPanel: function(panelName, noCreate)
  208.     {
  209.         var panelType = Firebug.getPanelType(panelName);
  210.         if (!panelType && this.panelTypeMap)
  211.             panelType = this.panelTypeMap[panelName];  // context local panelType
  212.         //if (FBTrace.DBG_PANELS)                                                                                       /*@expore*/
  213.         //    FBTrace.sysout("tabContext.getPanel name="+panelName+" noCreate="+noCreate+" panelType="+(panelType?panelType.prototype.name:"null")+"\n");  /*@expore*/
  214.         if (panelType)
  215.             return this.getPanelByType(panelType, noCreate);
  216.     },
  217.  
  218.     getPanelByType: function(panelType, noCreate)
  219.     {
  220.         if (!panelType || !this.panelMap)
  221.             return null;
  222.  
  223.         var panelName = panelType.prototype.name;
  224.         if ( this.panelMap.hasOwnProperty(panelName) )
  225.         {
  226.             var panel = this.panelMap[panelName];
  227.             //if (FBTrace.DBG_PANELS)
  228.             //    FBTrace.sysout("tabContext.getPanelByType panel in panelMap, .invalid="+panel.invalid+"\n");
  229.             if (panel.invalid)
  230.             {
  231.                 var doc = this.chrome.getPanelDocument(panelType);
  232.                 panel.reattach(doc);
  233.                 delete panel.invalid;
  234.             }
  235.  
  236.             return panel;
  237.         }
  238.         else if (!noCreate)
  239.         {
  240.             //if (FBTrace.DBG_PANELS) FBTrace.sysout("tabContext.getPanelByType panel NOT in panelMap\n");
  241.             var panel = new panelType();  // This is why panels are defined by prototype inheritance
  242.             var doc = this.chrome.getPanelDocument(panelType);
  243.             panel.initialize(this, doc);
  244.  
  245.             return this.panelMap[panel.name] = panel;
  246.         }
  247.     },
  248.  
  249.     setPanel: function(panelName, panel)  // allows a panel from one context to be used in other contexts.
  250.     {
  251.         if (panel)
  252.             this.panelMap[panelName] = panel;
  253.         else
  254.             delete this.panelMap[panelName];
  255.     },
  256.  
  257.     invalidatePanels: function()
  258.     {
  259.         if (!this.invalidPanels)
  260.             this.invalidPanels = {};
  261.  
  262.         for (var i = 0; i < arguments.length; ++i)
  263.         {
  264.             var panelName = arguments[i];
  265.             var panel = this.getPanel(panelName, true);
  266.             if (panel && !panel.noRefresh)
  267.                 this.invalidPanels[panelName] = 1;
  268.         }
  269.  
  270.         if (this.refreshTimeout)
  271.         {
  272.             this.clearTimeout(this.refreshTimeout);
  273.             delete this.refreshTimeout;
  274.         }
  275.  
  276.         this.refreshTimeout = this.setTimeout(bindFixed(function()
  277.         {
  278.             var invalids = [];
  279.  
  280.             for (var panelName in this.invalidPanels)
  281.             {
  282.                 var panel = this.getPanel(panelName, true);
  283.                 if (panel)
  284.                 {
  285.                     if (panel.visible && !panel.editing)
  286.                         panel.refresh();
  287.                     else
  288.                         panel.needsRefresh = true;
  289.  
  290.                     // If the panel is being edited, we'll keep trying to
  291.                     // refresh it until editing is done
  292.                     if (panel.editing)
  293.                         invalids.push(panelName);
  294.                 }
  295.             }
  296.  
  297.             delete this.invalidPanels;
  298.             delete this.refreshTimeout;
  299.  
  300.             // Keep looping until every tab is valid
  301.             if (invalids.length)
  302.                 this.invalidatePanels.apply(this, invalids);
  303.         }, this), refreshDelay);
  304.     },
  305.  
  306.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  307.  
  308.     setTimeout: function()
  309.     {
  310.         if (setTimeout == this.setTimeout)
  311.             throw new Error("setTimeout recursion");
  312.         var timeout = setTimeout.apply(top, arguments);
  313.  
  314.         if (!this.timeouts)
  315.             this.timeouts = {};
  316.  
  317.         this.timeouts[timeout] = 1;
  318.  
  319.         return timeout;
  320.     },
  321.  
  322.     clearTimeout: function(timeout)
  323.     {
  324.         clearTimeout(timeout);
  325.  
  326.         if (this.timeouts)
  327.             delete this.timeouts[timeout];
  328.     },
  329.  
  330.     setInterval: function()
  331.     {
  332.         var timeout = setInterval.apply(top, arguments);
  333.  
  334.         if (!this.intervals)
  335.             this.intervals = {};
  336.  
  337.         this.intervals[timeout] = 1;
  338.  
  339.         return timeout;
  340.     },
  341.  
  342.     clearInterval: function(timeout)
  343.     {
  344.         clearInterval(timeout);
  345.  
  346.         if (this.intervals)
  347.             delete this.intervals[timeout];
  348.     },
  349.  
  350.     delay: function(message, object)
  351.     {
  352.         this.throttle(message, object, null, true);
  353.     },
  354.  
  355.     // queue the call |object.message(arg)| or just delay it if forceDelay
  356.     throttle: function(message, object, args, forceDelay)
  357.     {
  358.         if (!this.throttleInit)
  359.         {
  360.             this.throttleBuildup = 0;
  361.             this.throttleQueue = [];
  362.             this.throttleTimeout = 0;
  363.             this.lastMessageTime = 0;
  364.             this.throttleInit = true;
  365.         }
  366.  
  367.         if (!forceDelay)
  368.         {
  369.             if (!Firebug.throttleMessages)
  370.             {
  371.                 message.apply(object, args);
  372.                 return false;
  373.             }
  374.  
  375.             // Count how many messages have been logged during the throttle period
  376.             var logTime = new Date().getTime();
  377.             if (logTime - this.lastMessageTime < throttleTimeWindow)
  378.                 ++this.throttleBuildup;
  379.             else
  380.                 this.throttleBuildup = 0;
  381.  
  382.             this.lastMessageTime = logTime;
  383.  
  384.             // If the throttle limit has been passed, enqueue the message to be logged later on a timer,
  385.             // otherwise just execute it now
  386.             if (!this.throttleQueue.length && this.throttleBuildup <= throttleMessageLimit)
  387.             {
  388.                 message.apply(object, args);
  389.                 return false;
  390.             }
  391.         }
  392.  
  393.         this.throttleQueue.push(message, object, args);
  394.  
  395.         if (this.throttleTimeout)
  396.             this.clearTimeout(this.throttleTimeout);
  397.  
  398.         var self = this;
  399.         this.throttleTimeout =
  400.             this.setTimeout(function() { self.flushThrottleQueue(); }, throttleInterval);
  401.         return true;
  402.     },
  403.  
  404.     flushThrottleQueue: function()
  405.     {
  406.         var queue = this.throttleQueue;
  407.  
  408.         if (!queue[0])
  409.             FBTrace.sysout("tabContext.flushThrottleQueue no queue[0]", queue);
  410.  
  411.         var max = throttleFlushCount * 3;
  412.         if (max > queue.length)
  413.             max = queue.length;
  414.  
  415.         for (var i = 0; i < max; i += 3)
  416.             queue[i].apply(queue[i+1], queue[i+2]);
  417.  
  418.         queue.splice(0, throttleFlushCount*3);
  419.  
  420.         if (queue.length)
  421.         {
  422.             var self = this;
  423.             this.throttleTimeout =
  424.                 this.setTimeout(function f() { self.flushThrottleQueue(); }, throttleInterval);
  425.         }
  426.         else
  427.             this.throttleTimeout = 0;
  428.     }
  429. };
  430.  
  431. // ************************************************************************************************
  432. // Local Helpers
  433.  
  434. function createPanelType(name, url, title, parentPanel)
  435. {
  436.     var panelType = new Function("");
  437.     panelType.prototype = extend(new Firebug.PluginPanel(),
  438.     {
  439.         name: name,
  440.         url: url,
  441.         title: title ? title : "...",
  442.         parentPanel: parentPanel
  443.     });
  444.  
  445.     return panelType;
  446. }
  447.  
  448. function createPanelName(url)
  449. {
  450.     return url.replace(/[:\\\/\s\.\?\=\&\~]/g, "_");
  451. }
  452.  
  453. // ************************************************************************************************
  454.  
  455. }});
  456.